/*-----------------------------------------------------------------------------
 * File: sr_router.h
 * Date: ?
 * Authors: Guido Apenzeller, Martin Casado, Virkam V.
 * Contact: casado@stanford.edu
 *
 *---------------------------------------------------------------------------*/

#ifndef SR_ROUTER_H
#define SR_ROUTER_H

#include <netinet/in.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdbool.h>

#include "sr_protocol.h"

/* we dont like this debug , but what to do for varargs ? */
#ifdef _DEBUG_
#define Debug(x, args...) printf(x, ## args)
#define DebugMAC(x) \
  do { int ivyl; for(ivyl=0; ivyl<5; ivyl++) printf("%02x:", \
  (unsigned char)(x[ivyl])); printf("%02x",(unsigned char)(x[5])); } while (0)
#else
#define Debug(x, args...) do{}while(0)
#define DebugMAC(x) do{}while(0)
#endif

#define INIT_TTL 255
#define PACKET_DUMP_SIZE 1024
#define MAX_HOSTS 32
#define CACHE_SIZE 32
#define CACHE_TIMEOUT 15000000
#define CACHE_SEC_TIMEOUT 15
#define MAX_SEND_PACKET 5

/* forward declare */
struct sr_if;
struct sr_rt;

/* ----------------------------------------------------------------------------
 * struct sr_instance
 *
 * Encapsulation of the state for a single virtual router.
 *
 * -------------------------------------------------------------------------- */

struct sr_instance
{
    int  sockfd;   /* socket to server */
    char user[32]; /* user name */
    char host[32]; /* host name */
    unsigned short topo_id;
    struct sockaddr_in sr_addr; /* address to server */
    struct sr_if* if_list; /* list of interfaces */
    struct sr_rt* routing_table; /* routing table */
    uint32_t cacheHosts[MAX_HOSTS]; /* array of host names */
    uint8_t packetCacheEntryStatus[MAX_HOSTS][CACHE_SIZE]; /* 2d array that signals if a packet is filled or not */
    uint8_t *packetCache[MAX_HOSTS][CACHE_SIZE]; /* 2d array of packets one cache per host */
    struct sr_arp_cache_entry *arp_cache[MAX_HOSTS]; /* arp cache (one entry per host reachable by any interface */
    clock_t timers[MAX_HOSTS];
    int packetsSinceAck[MAX_HOSTS];
    FILE* logfile;
};

struct sr_arp_cache_entry
{
	uint32_t arp_addr;
	unsigned int arp_age;
	unsigned char arp_dha[ETHER_ADDR_LEN]; /* if arp_dha = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} */
	unsigned int arp_if_id;
};

/* -- sr_main.c -- */
int sr_verify_routing_table(struct sr_instance* sr);

/* -- sr_vns_comm.c -- */
int sr_send_packet(struct sr_instance* , uint8_t* , unsigned int , const char*);
int sr_connect_to_server(struct sr_instance* ,unsigned short , char* );
int sr_read_from_server(struct sr_instance* );

/* -- sr_router.c -- */
void sr_init(struct sr_instance* );
void sr_handlepacket(struct sr_instance* , uint8_t * , unsigned int , char* );

/* -- sr_if.c -- */
void sr_add_interface(struct sr_instance* , const char* );
void sr_set_ether_ip(struct sr_instance* , uint32_t );
void sr_set_ether_addr(struct sr_instance* , const unsigned char* );
void sr_print_if_list(struct sr_instance* );




/* my function */
struct sr_ethernet_hdr *get_ethernet_hdr(uint8_t *);
struct sr_arphdr *get_arp_hdr(uint8_t *);
struct ip *get_ip_hdr(uint8_t *);
struct sr_icmphdr *get_icmp_hdr(uint8_t *);
struct sr_udphdr *get_udp_hdr(uint8_t *);
struct tcp *get_tcp_hdr(uint8_t *);

void set_ethernet_hdr(uint8_t *packet, struct sr_ethernet_hdr *etherhdr);
void set_arp_hdr(uint8_t *packet, struct sr_arphdr *arphdr);
void set_ip_hdr(uint8_t *packet, struct ip *ip_hdr);
void set_icmp_hdr(uint8_t *packet, struct sr_icmphdr *icmphdr, struct ip *ip_hdr);

struct sr_icmphdr *create_icmp_header(uint8_t , uint8_t , uint16_t ,uint16_t, uint16_t, uint16_t);
struct ip *create_ip(unsigned int, unsigned int, uint8_t, uint16_t, uint8_t, struct in_addr src, struct in_addr dst, uint16_t);
struct sr_ethernet_hdr *create_ethernet_header(uint8_t dhost[], uint8_t shost[], uint16_t type);
struct tcp *create_tcp_hdr(uint16_t, uint16_t, uint32_t, uint32_t, uint8_t, uint16_t);
struct sr_udphdr *create_udp_hdr(uint16_t,uint16_t,uint16_t);
uint8_t *create_icmp_msg(struct sr_instance *sr, uint8_t *, struct in_addr src, struct in_addr dst, unsigned int , char *, uint8_t, uint8_t);
struct sr_arphdr *create_arp_hdr(unsigned short arp_type, unsigned char send_hard_addr[], uint32_t send_ip, unsigned char targ_hard_addr[], uint32_t targ_ip);
uint8_t *create_arp_reply(struct sr_instance *sr, uint32_t src_ip,  unsigned char src_hdwr[], uint32_t targ_ip, unsigned char targ_hdwr[], unsigned short op);

void sr_handle_tcp_packet(struct sr_instance* sr, unsigned int, char *, struct tcp *tcphdr, uint8_t *);
void sr_handle_udp_packet(struct sr_instance* sr, unsigned int, char *, struct sr_udphdr *updhdr, uint8_t *);
void sr_handle_arp_packet(struct sr_instance* sr, unsigned int , char *, struct sr_arphdr *arphdr, uint8_t *);
void sr_handle_icmp_packet(struct sr_instance* sr, unsigned int, char *, struct sr_icmphdr *icmphdr, uint8_t *);
void sr_route_packet(struct sr_instance *, uint8_t *, unsigned int, char *);

uint16_t icmp_checksum( uint8_t type,uint8_t code,uint16_t id,uint16_t seq);
uint16_t calculate_checksum(uint16_t len_ip_header, uint16_t buff[]);
void insert_packet_into_arp_cache(struct sr_instance* sr, int, int ,uint8_t*, unsigned int);
bool test_staleness(struct sr_instance* sr, unsigned int);
void sr_handle_staleness(struct sr_instance* sr, unsigned int);
void sr_set_arp_cache_entry(struct sr_instance* sr, uint32_t ip_addr, unsigned char hdw_addr[], unsigned int if_id);
int sr_get_cache_host_id(struct sr_instance* sr, uint32_t hostIP);
uint32_t sr_get_host_ip_from_ether_addr(struct sr_instance* sr, unsigned char host_addr[]);
void route_host_cache_head_packet(struct sr_instance* sr, int);
void sr_init_arp_cache(struct sr_instance *sr);
#endif /* SR_ROUTER_H */
